﻿//Plugin Config Utility services library

(function (ns)
{
    //Members

    var ODataEndpoint = null;

    this.Constants = {
        MSG_NO_QUERIES_FOUND: "No results to display",
        MSG_GENERAL_FAILURE: "Something went wrong :( ",
        MSG_PROCESSING: "Working on it...",
        MSG_ERR_NO_CONTEXT: "No context found, operation aborted",
        MSG_SETTING_CONFIG_SUCCESS: "Step configuration updated successfuly :)",
        MSG_ERR_SETTING_CONFIG: "Step configuration update failure :(",
        MSG_INF_MULTIPLE_STEPS_SELECTED: "To display step configuration details, select a single step",
        MSG_WRN_MULTIPLE_STEPS_SELECTED: "Specified configuration will be applied to all selected steps. Continue?",
        ODATA_ENDPOINT_SUFFIX: "/api/data/v8.0/"
    };

    $(document).ready(function ()
    {
        //set OData end-point        
        ODataEndpoint = getContext().getClientUrl() + Constants.ODATA_ENDPOINT_SUFFIX;        

        //handle Plugin assembly selection event                         
        $("#pluginAssembliesSelect").change(function ()
        {
            handleMasterSelectEvent("pluginAssembliesSelect", "pluginStepsSelect", "name", "sdkmessageprocessingstepid");
        });

        //handle processing step selection event                         
        $("#pluginStepsSelect").change(function ()
        {
            handleDetailSelectEvent();
        });

        //handle Workflow assembly filter change event 
        $("#hideCWAAssembliesInd").change(function ()
        {
            var hideSystemAssembliesInd = $("#hideSystemAssembliesInd").is(':checked');
            var hideCWAAssembliesInd = this.checked;

            //clear previous notifications
            $("#userMessages").text("");

            $("#pluginStepsSelect").empty();
            $("#securedConfigText").val("");
            $("#unsecuredConfigText").val("");

            bindPluginAssembliesSelect(hideSystemAssembliesInd,
                hideCWAAssembliesInd);
        });

        //handle System assembly filter change event 
        $("#hideSystemAssembliesInd").change(function ()
        {
            var hideCWAAssembliesInd = $("#hideCWAAssembliesInd").is(':checked');
            var hideSystemAssembliesInd = this.checked;

            //clear previous notifications
            $("#userMessages").text("");

            $("#pluginStepsSelect").empty();
            $("#securedConfigText").val("");
            $("#unsecuredConfigText").val("");

            bindPluginAssembliesSelect(hideSystemAssembliesInd,
                hideCWAAssembliesInd);
        });

        //handle operation execution event
        $("#executeAction").click(handleOperationExecution);

        try
        {
            //retrive and bind plugin assemblies data 
            bindPluginAssembliesSelect(true, true);
        }
        catch (error)
        {
            //nottify user with error message
            notifyError(error);
        }
    });

    //extract Global Context object 
    getContext = function ()
    {
        var result = null;

        if (typeof GetGlobalContext != "undefined")
        {
            result = GetGlobalContext();
        }
        else
        {
            if (typeof Xrm != "undefined")
            {
                result = Xrm.Page.context;
            }
            else
            {
                throw new Error(Constants.ERR_NO_CONTEXT);
            }
        }

        return result;
    }

    bindPluginAssembliesSelect = function (hideSystemAssemblies, hideCWAAssemblies)
    {
        //retrieve registered Plug-in assemblies
        var PluginAssemblies = retrievePluginAssemblies(hideSystemAssemblies, hideCWAAssemblies);

        //bind data to system users list control
        bindEntityCollToSelectControl("pluginAssembliesSelect", PluginAssemblies, "assemblyname", "_pluginassemblyid_value");
    }

    //retrieve registered Plug-in assemblies using FetchXML & Web API
    retrievePluginAssemblies = function (hideSystemAssembly, hideCWFAssembly)
    {
        var result = null;
        var systemAssemblyCondition = "";
        var CWAAssemblyCondition = "";

        //add system assemblies filter 
        if (hideSystemAssembly == true)
        {
            systemAssemblyCondition = "<link-entity name='systemuser' from='systemuserid' to='createdby' alias='aa'>" +
                                            "<filter type='and'>" +
                                                "<condition attribute='fullname' operator='ne' value='system' />" +
                                            "</filter>" +
                                       "</link-entity>";
        }

        //add CWA assemblies filter 
        if (hideCWFAssembly == true)
        {

            CWAAssemblyCondition = "<filter type='and'>" +
                                        "<condition attribute='isworkflowactivity' operator='eq' value='false'></condition>" +
                                   "</filter>";
        }

        //define query        
        var query = "<fetch distinct='true'>" +
                        "<entity name='plugintype'>" +
                            "<attribute name='assemblyname'></attribute>" +
                            "<attribute name='pluginassemblyid'></attribute>" +
                            systemAssemblyCondition +
                            CWAAssemblyCondition +
                        "</entity>" +
                    "</fetch>";

        //define request 
        var req = new XMLHttpRequest();
        req.open("GET", encodeURI(ODataEndpoint + "plugintypes" + "?fetchXml=" + escape(query)), false);
        req = setConstantRequestHeaders(req);
        req.onreadystatechange = function ()
        {
            if (this.readyState == 4 /* complete */)
            {
                req.onreadystatechange = null;
                if (this.status == 200)
                {
                    data = JSON.parse(this.response);
                    result = data.value;
                }
                else
                {
                    var error = JSON.parse(this.response).error;
                    notifyError(error.message);
                }
            }
        };
        req.send();

        return result;
    }

    //handle select contril population event 
    handleMasterSelectEvent = function (masterControlId, detailsControlId, detailNameAtt, detailIdAtt)
    {
        //extract selected record id
        var masterId = $("#" + masterControlId).val();

        //clear previous notifications
        $("#userMessages").text("");

        //empty details control options
        $("#" + detailsControlId).empty();

        //retrieve selected id related itmes
        var relatedItems = retrieveRelatedItems(masterId, "sdkmessageprocessingsteps");

        //bind data to processing steps list control            
        bindEntityCollToSelectControl(detailsControlId,
            relatedItems,
            detailNameAtt,
            detailIdAtt,
            "_sdkmessageprocessingstepsecureconfigid_value");
    }

    //bind data to select control
    bindEntityCollToSelectControl = function (controlId, data, labelDataItem, valueDataItem, additionalInfo)
    {
        //empty current displayed options
        $("#" + controlId).empty();

        //handle empty data collection 
        if ((data == null) || (data.length == 0))
        {
            $("#" + controlId)
                .append($("<option></option>")
                .attr("value", -1)
                .text(Constants.MSG_NO_QUERIES_FOUND));

            $("#" + controlId).prop('disabled', true);
        }
        else
        {
            for (i = 0; i < data.length ; i++)
            {
                var optionText = data[i][labelDataItem];

                $("#" + controlId)
                    .append($("<option></option>")
                    .attr("value", data[i][valueDataItem])
                    .attr("secureConfigId", data[i][additionalInfo])
                    .text(optionText));
            }

            $("#" + controlId).prop('disabled', false);
        }
    }

    //retrieve selected id related itmes
    retrieveRelatedItems = function (masterId, itemEntity)
    {
        var result = null;

        //define query
        var query = "<fetch distinct='true'>" +
                        "<entity name='sdkmessageprocessingstep'>" +
                        "<attribute name='sdkmessageprocessingstepid' />" +
                        "<attribute name='name' />" +
                        "<attribute name='configuration' />" +
                        "<attribute name='sdkmessageprocessingstepsecureconfigid' />" +
                        "<link-entity name='plugintype' from='plugintypeid' to='plugintypeid' alias='aa'>" +
                            "<filter type='and'>" +
                                "<condition attribute='pluginassemblyid' operator='eq' value='{0}' />" +
                            "</filter>" +
                        "</link-entity>" +
	                    "</entity>" +
                    "</fetch>";

        query = query.replace("{0}", masterId);

        //define request 
        var req = new XMLHttpRequest();
        req.open("GET", encodeURI(ODataEndpoint + itemEntity + "?fetchXml=" + escape(query)), false);
        req = setConstantRequestHeaders(req);
        req.onreadystatechange = function ()
        {
            if (this.readyState == 4 /* complete */)
            {
                req.onreadystatechange = null;
                if (this.status == 200)
                {
                    data = JSON.parse(this.response);
                    result = data.value;
                }
                else
                {
                    var error = JSON.parse(this.response).error;
                    notifyError(error.message);
                }
            }
        };
        req.send();

        return result;
    }

    //handle processing step selection event 
    handleDetailSelectEvent = function ()
    {
        $("#securedConfigText").val("");
        $("#unsecuredConfigText").val("");

        //extract selected items id's
        var selectedItems = $('#pluginStepsSelect').val();

        //display single step configuration 
        if (selectedItems.length == 1)
        {
            bindStepConfiguration(selectedItems[0]);

            //hide bulk selection notification
            $("#pluginStepsSelectMessages").text("");

            //hide notification 
            $("#userMessages").text("");
        }
        else
        {
            //clear previous notifications
            $("#userMessages").text("");

            //display bulk selection notification
            $("#pluginStepsSelectMessages").text(Constants.MSG_INF_MULTIPLE_STEPS_SELECTED);
        }
    }

    //retrieve and bind step configuration data
    bindStepConfiguration = function (processingStepId, secureConfigId, unsecureConfig)
    {
        if (processingStepId != null)
        {
            //retrieve step configuration details
            var procrssingStepDetails = retrieveProcessingStepDetails(processingStepId);

            //test for secure config
            if (procrssingStepDetails[0]._sdkmessageprocessingstepsecureconfigid_value)
            {
                var secureConfig = retrieveSecureConfig(procrssingStepDetails[0]._sdkmessageprocessingstepsecureconfigid_value);
                $("#securedConfigText").val(secureConfig[0].secureconfig);
            }

            $("#unsecuredConfigText").val(procrssingStepDetails[0].configuration);
        }
    }

    //retrieve selected id related itmes
    retrieveProcessingStepDetails = function (processingStepId)
    {
        var result = null;

        //define query
        var query = "<fetch>" +
                        "<entity name='sdkmessageprocessingstep'>" +
                        "<attribute name='configuration' />" +
                        "<attribute name='sdkmessageprocessingstepsecureconfigid' />" +
                            "<filter type='and'>" +
                                "<condition attribute='sdkmessageprocessingstepid' operator='eq' value='{0}' />" +
                            "</filter>" +
	                    "</entity>" +
                    "</fetch>";

        query = query.replace("{0}", processingStepId);

        //define request 
        var req = new XMLHttpRequest();
        req.open("GET", encodeURI(ODataEndpoint + "sdkmessageprocessingsteps" + "?fetchXml=" + escape(query)), false);
        req = setConstantRequestHeaders(req);
        req.onreadystatechange = function ()
        {
            if (this.readyState == 4 /* complete */)
            {
                req.onreadystatechange = null;
                if (this.status == 200)
                {
                    data = JSON.parse(this.response);
                    result = data.value;
                }
                else
                {
                    var error = JSON.parse(this.response).error;
                    notifyError(error.message);
                }
            }
        };
        req.send();

        return result;
    }

    //retrieve secure config 
    retrieveSecureConfig = function (secureConfigId)
    {
        var result = null;

        //define query
        var query = "<fetch>" +
                        "<entity name='sdkmessageprocessingstepsecureconfig'>" +
                        "<attribute name='secureconfig' />" +
                            "<filter type='and'>" +
                                "<condition attribute='sdkmessageprocessingstepsecureconfigid' operator='eq' value='{0}' />" +
                            "</filter>" +
	                    "</entity>" +
                    "</fetch>";

        query = query.replace("{0}", secureConfigId);

        //define request 
        var req = new XMLHttpRequest();
        req.open("GET", encodeURI(ODataEndpoint + "sdkmessageprocessingstepsecureconfigs" + "?fetchXml=" + escape(query)), false);
        req = setConstantRequestHeaders(req);
        req.onreadystatechange = function ()
        {
            if (this.readyState == 4 /* complete */)
            {
                req.onreadystatechange = null;
                if (this.status == 200)
                {
                    data = JSON.parse(this.response);
                    result = data.value;
                }
                else
                {
                    var error = JSON.parse(this.response).error;
                    notifyError(error.message);
                }
            }
        };
        req.send();

        return result;
    }

    //handle operation execution
    handleOperationExecution = function ()
    {
        //extract selected items id's
        var selectedItems = $('#pluginStepsSelect').val();

        //display multipe steps configuration warning
        if (selectedItems.length > 1)
        {
            if (confirm(Constants.MSG_WRN_MULTIPLE_STEPS_SELECTED))
            {
                updateSteponfig();
            }
        }
        else
        {
            updateSteponfig();
        }
    }

    //update step configuration
    updateSteponfig = function ()
    {
        //extract target values 
        var secureConfigText = $("#securedConfigText").val();
        var unsecureConfigText = $("#unsecuredConfigText").val();
        var unsecureResult = null;
        var secureResult = null;

        $('#pluginStepsSelect').find(':selected').each(function ()
        {
            //extract selected processing step id         
            var processingStepId = this.value;
            var sdkmessageprocessingstepsecureconfigId = $(this).attr("secureConfigId");

            //update unsecure config 
            unsecureResult = updateRecordAttribute(
                "sdkmessageprocessingsteps",
                processingStepId,
                "configuration",
                unsecureConfigText);

            //create or update secure config record
            if (sdkmessageprocessingstepsecureconfigId != null)
            {
                //update existing secure config record
                secureResult = updateRecordAttribute(
                    "sdkmessageprocessingstepsecureconfigs",
                    sdkmessageprocessingstepsecureconfigId,
                    "secureconfig",
                    secureConfigText);
            }
            else if ((secureConfigText != null) && (secureConfigText != ""))
            {
                //create new secure config record 
                var secureConfigRecord = { secureconfig: secureConfigText };

                secureconfigId =
                    createRecord("sdkmessageprocessingstepsecureconfigs", secureConfigRecord);

                var processingStep = { "sdkmessageprocessingstepsecureconfigid@odata.bind": "/sdkmessageprocessingstepsecureconfigs(" + secureconfigId + ")" };

                //relate step to secure config record 
                secureResult = updateRecord(
                    "sdkmessageprocessingsteps",
                    processingStepId,
                    processingStep);
            }

            //norify result 
            if ((secureResult == "operation success") && (unsecureResult == "operation success"))
            {
                //notify success 
                $("#userMessages").text(Constants.MSG_SETTING_CONFIG_SUCCESS);
            }
            else
            {
                //notify error 
                notifyError(Constants.MSG_ERR_SETTING_CONFIG);
            }
        });
    }

    //create record 
    createRecord = function (targetEntity, targetRecord)
    {
        var result = null;

        //define request
        var req = new XMLHttpRequest();
        req.open("POST", ODataEndpoint + targetEntity, false);
        req.setRequestHeader("Accept", "application/json");
        req.setRequestHeader("Content-Type", "application/json; charset=utf-8");
        req.setRequestHeader("OData-MaxVersion", "4.0");
        req.setRequestHeader("OData-Version", "4.0");
        req.onreadystatechange = function ()
        {
            if (this.readyState == 4)
            {
                req.onreadystatechange = null;

                if (this.status == 204)
                {
                    var recordUri = this.getResponseHeader("OData-EntityId");
                    result = recordUri.substr(recordUri.length - 38).substring(1, 37);
                }
                else
                {
                    var error = JSON.parse(this.response).error;
                    result = error.message;
                }
            }
        };

        //send request 
        req.send(window.JSON.stringify(targetRecord));

        return result;
    }

    //update record's specific attribute
    updateRecordAttribute = function (targetEntity, targetRecordId, targetAttributeName, targetAttributeValue)
    {
        var result = null;

        var record = { "value": targetAttributeValue };

        var req = new XMLHttpRequest();
        req.open("PUT", ODataEndpoint + targetEntity + "(" + targetRecordId + ")/" + targetAttributeName, false);
        req.setRequestHeader("Accept", "application/json");
        req.setRequestHeader("Content-Type", "application/json; charset=utf-8");
        req.setRequestHeader("OData-MaxVersion", "4.0");
        req.setRequestHeader("OData-Version", "4.0");
        req.onreadystatechange = function ()
        {
            if (this.readyState == 4 /* complete */)
            {
                req.onreadystatechange = null;
                if (this.status == 204)
                {
                    result = "operation success";
                }
                else
                {
                    var error = JSON.parse(this.response).error;
                    result = error.message;
                }
            }
        };
        req.send(JSON.stringify(record));

        return result;
    }

    ///Update multiple attributes 
    updateRecord = function (targetEntity, targetRecordId, targetRecord)
    {
        var result = null;

        var req = new XMLHttpRequest();
        req.open("PATCH", ODataEndpoint + targetEntity + "(" + targetRecordId + ")", false);
        req.setRequestHeader("Accept", "application/json");
        req.setRequestHeader("Content-Type", "application/json; charset=utf-8");
        req.setRequestHeader("OData-MaxVersion", "4.0");
        req.setRequestHeader("OData-Version", "4.0");
        req.onreadystatechange = function ()
        {
            if (this.readyState == 4 /* complete */)
            {
                req.onreadystatechange = null;
                if (this.status == 204)
                {
                    result = "operation success";
                }
            }
            else
            {
                var error = JSON.parse(this.response).error;
                result = error.message;
            }
        }

        req.send(JSON.stringify(targetRecord));

        return result;
    }

    //add constant XMLHTTPRequest headers to XMLHTTPRequest
    setConstantRequestHeaders = function (request)
    {
        var result = request;

        request.setRequestHeader("Accept", "application/json");
        request.setRequestHeader("Content-Type", "application/json; charset=utf-8");
        request.setRequestHeader("OData-MaxVersion", "4.0");
        request.setRequestHeader("OData-Version", "4.0");

        return result;
    }

    //notify user with error message 
    notifyError = function (error)
    {
        $("#userMessages").removeClass('userSuccessMessages').addClass('userFailureMessages');
        $("#userMessages").text(error.message);
    }

})(window.PluginConfigUtility = window.PluginConfigUtility || {});
